#include "app_bt.h"
#include "string.h"
#include "log.h"
#include "app_common.h"
#include "app_serial.h"
#include <unistd.h>
#include <fcntl.h>

static char bt_send_header[8] = {0x41, 0x54, 0x2b, 0x4d, 0x45, 0x53, 0x48, 0x00};
static char bt_send_tailer[2] = {0x0d, 0x0a};
static char bt_receive_header[2] = {0xf1, 0xdd};
/**
 * @brief 蓝牙初始化配置
 * 1  设置 预处理方法 2个  写前  和读后
 * 2  设置 蓝牙相关的参数默认值  波特率 网络id 网络u短地址 网络名称
 *
 *
 */
int app_bt_init(device_t *device)
{ // 1  设置 预处理方法 2个  写前  和读后
	device->pre_write = app_bt_pre_write;
	device->post_read = app_bt_post_read;
	// 2  设置 蓝牙相关的参数默认值  波特率 网络id 网络u短地址 网络名称
	int ret = app_bt_set_default_all(device);

	return ret;
}

/**
 * @brief 蓝牙写前预处理
 * @param 处理前的数据
 * @param 处理前的数据长度
 * @return 预处理后的数据ng长度
 *
 *   把字节数组转换为蓝牙数据
   * 准备向设备写前处理数据来满足蓝牙发送数据的要求
 * 当前data: conn_type id_len msg_len id msg (01 02 03 ff ff 11 22 33)
 * 蓝牙发送数据格式:  41 54 2b 4d 45 53 48 00 ff ff 11 22 33 0d 0a
  示例：41 54 2b 4d 45 53 48 00 ff ff 41 42 43 0d 0a
		41 54 2b 4d 45 53 48 00： AT+MESH（固定头部）
		ff ff: 对端的MADDR（如果是FFFF代表群发）
		11 22 33: 要发送的数据（不超过12字节）
		0d 0a：\r\n（固定结尾）

 */
int app_bt_pre_write(char *data, int data_len)
{
	char data_new[100];
	// 提取长度
	int id_len = data[1];
	int msg_len = data[2];

	// 帧头
	memcpy(data_new, bt_send_header, 8);
	// id
	memcpy(data_new + 8, data + 3, id_len);
	// msg
	memcpy(data_new + 8 + id_len, data + 3 + id_len, msg_len);
	// 结尾
	memcpy(data_new + 8 + id_len + msg_len, bt_send_tailer, 2);

	int data_new_len = 8 + id_len + msg_len + 2;
	// 覆盖回原始数据
	memcpy(data, data_new, data_new_len);

	return data_new_len;
}

/**
 * @brief 蓝牙读后预处理
 * @param 处理前的数据
 * @param 处理前的数据长度
 * @return 预处理后的数据长度
 *
 *  * 读取蓝牙中的数据  转换为 字节数组
 *
 * 从设备读取数据后处理数据来满足网关应用的要求
 * 接收方得到数据1（4）：ok\r\n
 * 接收方得到数据2（3 + [2]）：f1 dd 07 23 23 ff ff 41 42 43
	f1 dd : 固定的头部
	07： 之后数据的长度（5-16之间）
	23 23：对端（发送方）的MADDR
	ff ff: 我的MADDR或ffff(群发)
	41 42 43：发送的数据
	转换为：01 02 03 23 23 41 42 43  conn_type id_len msg_len id msg
	//1   把收到的数据加入缓冲区
	//2   先看当前缓冲区总数据量够不够 至少要有8个字节
	//3   如果够8 个字节
	 //4  从缓冲区中逐个找到帧头 f1 dd
	 //5  如果找到帧头  清理掉帧头之前的数据
	 //6  清理后是否够8个字节 取帧头后的第一个字节 标识了之后数据的长度 根据此长度判断当前q缓冲区是否有整帧数据。
	 //7  如果有整帧数据  则把整帧数据拷贝到data中 （需要拼接）
	 //8 把改条数据从缓冲区中删除 调整缓冲区长度
	 //9 返回数据长度
 */

static char data_buff[100];
static int data_buff_len = 0;

// 从缓冲区清理指定长度的数据
static void app_bt_clear_data_buff(int len)
{
	memmove(data_buff, data_buff + len, data_buff_len - len); // 平移覆盖
	data_buff_len -= len;
}

int app_bt_post_read(char *data, int data_len)
{
	// 1   把收到的数据加入缓冲区
	memcpy(data_buff + data_buff_len, data, data_len);
	data_buff_len += data_len;
	app_common_print_hex(data_buff, data_buff_len);

	// 2   先看当前缓冲区总数据量够不够 至少要有8个字节
	if (data_buff_len < 8)
	{
		return 0;
	}
	// 3   如果够8 个字节
	// 4  从缓冲区中逐个找到帧头 f1 dd
	for (int i = 0; i < data_buff_len - 1; i++)
	{
		if (data_buff[i] == bt_receive_header[0] && data_buff[i + 1] == bt_receive_header[1])
		{
			// 5  如果找到帧头  清理掉帧头之前的数据
			app_bt_clear_data_buff(i);

			// 6  清理后是否够8个字节 取 第3字节 标识了之后数据的长度 根据此长度判断当前q缓冲区是否有整帧数据。
			// 清理后不足8个字节退出
			if (data_buff_len < 8)
			{
				return 0;
			}
			int len_after_header = data_buff[2];
			if (data_buff_len < 3 + len_after_header) // 如果当前缓冲区不足 2（帧头）+ len_after_header（数据长度）
			{
				return 0;
			}
			// 以下存在整帧数据
			// 7  如果有整帧数据  则把整帧数据拷贝到data中 （需要拼接）
			data[0] = CONN_TYPE_BT;
			data[1] = 2;
			data[2] = len_after_header - 4;				  // 减去2个地址长度
			memcpy(data + 3, data_buff + 3, 2);			  // id
			memcpy(data + 3 + 2, data_buff + 7, data[2]); // msg

			// 8 把改条数据从缓冲区中删除 调整缓冲区长度
			app_bt_clear_data_buff(3 + len_after_header);

			// 9 返回数据长度
			int new_data_len = 3 + 2 + data[2]; // 3个帧头+ id固定2 +msg_len

			return new_data_len;
		}
	}
	return 0;
}

static int wait_ack(device_t *device)
{
	usleep(1000 * 50); // 等待50ms
	// 读取设备文件中的值 read
	char data_buff[4]; //  OK\r\n
	int read_size = read(device->dev_fd, data_buff, 4);
	printf("read wait_ack size: %d\n", read_size);
	app_common_print_hex(data_buff, 4);
	if (memcmp(data_buff, "OK\r\n", 4) == 0)
	{
		return 0;
	}
	return -1;
}

/**
 * @brief 蓝牙连通测试   AT\r\n
 * @param device_t *device
 * @return int 0:成功  -1:失败
 */
int app_bt_status(device_t *device)
{
	// 1 声明一个命令字符串
	char *cmd = "AT\r\n";
	// 2 把字符串写入串口设备文件中 write
	int ret = write(device->dev_fd, cmd, strlen(cmd));
	// 3 等待并且获取返回值
	return wait_ack(device);
}

/**
 * @brief 蓝牙芯片重启   AT+RESET\r\n
 * @param device_t *device
 * @return int 0:成功  -1:失败
 */
int app_bt_reset(device_t *device)
{
	// 1 声明一个命令字符串
	char *cmd = "AT+RESET\r\n";
	// 2 把字符串写入串口设备文件中 write
	int ret = write(device->dev_fd, cmd, strlen(cmd));
	// 3 等待并且获取返回值
	return wait_ack(device);
}

/**
 * @brief 设置波特率   AT+BAUD?\r\n
 * @param device_t *device  波特率
 * @return int 0:成功  -1:失败
 */
int app_bt_set_baud(device_t *device, BT_BAUD_RATE baud)
{
	// 1 声明一个命令字符串
	char cmd[20];
	sprintf(cmd, "AT+BAUD%c\r\n", baud);
	// 2 把字符串写入串口设备文件中 write
	int ret = write(device->dev_fd, cmd, strlen(cmd));
	// 3 等待并且获取返回值
	return wait_ack(device);
}

/**
 * @brief 设置网络id   AT+NETID?\r\n
 * @param device_t  *device  网络id
 * @return int 0:成功  -1:失败
 */
int app_bt_set_netid(device_t *device, char *netid)
{
	// 1 声明一个命令字符串
	char cmd[20];
	sprintf(cmd, "AT+NETID%s\r\n", netid);
	// 2 把字符串写入串口设备文件中 write
	int ret = write(device->dev_fd, cmd, strlen(cmd));
	// 3 等待并且获取返回值
	return wait_ack(device);
}

/**
 * @brief 设置网络地址  AT+MADDR?\r\n
 * @param device_t  *device  网络地址
 * @return int 0:成功  -1:失败
 */
int app_bt_set_maddr(device_t *device, char *maddr)
{
	// 1 声明一个命令字符串
	char cmd[20];
	sprintf(cmd, "AT+MADDR%s\r\n", maddr);
	// 2 把字符串写入串口设备文件中 write
	int ret = write(device->dev_fd, cmd, strlen(cmd));
	// 3 等待并且获取返回值
	return wait_ack(device);
}

/**
 * @brief 设置设备名称   AT+NAME?\r\n
 * @param device_t  *device  设备名称
 * @return int 0:成功  -1:失败
 */
int app_bt_set_name(device_t *device, char *name)
{
	// 1 声明一个命令字符串
	char cmd[20];
	sprintf(cmd, "AT+NAME%s\r\n", name);
	// 2 把字符串写入串口设备文件中 write
	int ret = write(device->dev_fd, cmd, strlen(cmd));
	// 3 等待并且获取返回值
	return wait_ack(device);
}

/**
* @brief 设置整体参数的默认值
* @param device_t  *device
* @return int 0:成功  -1:失败
* 	//  要考虑首次设置时 主控芯片和蓝牙芯片的波特率不一致  要对两者的连通性进行测试 如果不一致要进行调整波特再设其他值。
   //1 要调整一下串口阻塞模式 改为非阻塞模式
   //2 进行连通性测试 在此之前115200 如果测试不通过->蓝牙芯片的波特率 是9600
   //3 如果测试不通过
   // 3.1 把主控芯片的串口也先改为9600
   // 3.2 再进行一次 测试    如果还不通过 直接报错返回即可
   // 3.3 如果测试通过  向蓝牙芯片发送命令把波特率改为115200
   // 3.4 重启蓝牙芯片 等待2秒
   // 3.5 把主控芯片的波特率改为115200
   // 3.6 进行一次连通性测试  如果测试不通过  报错返回
   // 3.7 如果测试通过  则可以开始进行其他设置

   // 4 其他设置 ：设备名称  网络id  网络地址
*/
int app_bt_set_default_all(device_t *device)
{
	//  要考虑首次设置时 主控芯片和蓝牙芯片的波特率不一致  要对两者的连通性进行测试 如果不一致要进行调整波特再设其他值。
	// 1 要调整一下串口阻塞模式 改为非阻塞模式
	app_serial_set_block(device, 0);
	tcflush(device->dev_fd, TCIOFLUSH);
	// 2 进行连通性测试 在此之前115200 如果测试不通过->蓝牙芯片的波特率 是9600
	if (app_bt_status(device) != 0)
	{
		// 3 如果测试不通过
		//  3.1 把主控芯片的串口也先改为9600
		app_serial_set_baud(device, BR_9600);
		tcflush(device->dev_fd, TCIOFLUSH);
		//  3.2 再进行一次 测试    如果还不通过 直接报错返回即可
		if (app_bt_status(device) != 0)
		{
			log_error("改为9600后蓝牙芯片连通性依然测试不通过");
			return -1;
		}
		//  3.3 如果测试通过  向蓝牙芯片发送命令把波特率改为115200
		int ret = app_bt_set_baud(device, BT_BAUD_115200);
		if (ret != 0)
		{
			log_error("蓝牙芯片设置波特率失败");
			return -1;
		}
		// 3.4 重启蓝牙芯片 等待2秒
		ret = app_bt_reset(device);
		if (ret == 0)
		{
			log_info("蓝牙芯片重启成功");
		}
		else
		{
			log_error("蓝牙芯片重启失败");
		}
		sleep(2);
		// 3.5 把主控芯片的波特率改为115200
		app_serial_set_baud(device, BR_115200);
		tcflush(device->dev_fd, TCIOFLUSH);
		// 3.6 进行一次连通性测试  l两边都是115200 如果测试不通过  报错返回
		if (app_bt_status(device) != 0)
		{
			log_error("蓝牙芯片测试依然不通过 :115200");
			return -1;
		}
		// 3.7 如果测试通过  则可以开始进行其他设置
	}

	// 4 其他设置 ：设备名称  网络id  网络地址
	app_bt_set_name(device, "zhangchen_bt");
	app_bt_set_netid(device, "1234"); // 1234老师专用 学生：00组号
	app_bt_set_maddr(device, "0077"); // 不能跟组内其他蓝牙芯片相同（同组同学，下位机）
	// 5 串口恢复 为 阻塞 模式
	app_serial_set_block(device, 1);
	tcflush(device->dev_fd, TCIOFLUSH);
	return 0;
}
